<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>调度台副屏</title>
    <link rel="stylesheet" href="/static/elementui/lib-master/theme-chalk/index.css">
    <style>
        html, body {
            width: 100%;
            height: 100%;
            margin: 0px;
        }
    </style>
</head>
<body>
    <script src="https://cdn.jsdelivr.net/npm/@logicflow/core/dist/logic-flow.js"></script>

    <script src="https://cdn.jsdelivr.net/npm/@logicflow/extension/lib/Menu.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@logicflow/extension/lib/Group.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@logicflow/extension/lib/DndPanel.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@logicflow/extension/lib/Snapshot.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@logicflow/extension/lib/SelectionSelect.js"></script>

    <script src="/static/js/vue.js"></script>
    <script src="/static/elementui/lib-master/index.js"></script>
    <div id="app" style="width: 100%; height: 100%;">
        <div id="container"></div>;
    </div>

    <!-- Vue脚本 -->
    <script>

        ///////////////////// src/components/logicflowConfig/common/commonConfig.js start ////////////////
        // /**
        //  * 页面编辑状态选项 参考 http://logic-flow.org/api/editConfigModelApi.html
        //  */
         const editConfig = {
            // 是否为静默模式
            isSilentMode: false, // option value: true | false

            // 禁止缩放画布
            stopZoomGraph: false, // option value: true | false

            // 禁止鼠标滚动移动画布
            stopScrollGraph: false, // option value: true | false

            // 禁止拖动画布
            stopMoveGraph: false, // option value: true | false

            // 允许调整边
            adjustEdge: true, // option value: true | false

            // 只对折线生效，只允许调整边的中间线段，不允许调整与起点终点相连的线段
            adjustEdgeMiddle: true, // option value: true | false

            // 允许调整边起点/终点
            adjustEdgeStartAndEnd: false, // option value: true | false

            // 允许拖动节点
            adjustNodePosition: true, // option value: true | false

            // 隐藏节点所有锚点
            hideAnchors: false, // option value: true | false

            // 允许节点文本可以编辑
            nodeTextEdit: false, // option value: true | false

            // 允许边文本可以编辑
            edgeTextEdit: false, // option value: true | false

            // 允许节点文本可以拖拽
            nodeTextDraggable: false, // option value: true | false

            // 允许边文本可以拖拽
            edgeTextDraggable: false, // option value: true | false

            // 允许按照 meta 键多选元素
            metaKeyMultipleSelected: false // option value: true | false
            }

            /**
             * 自定义键盘快捷键 参考 http://logic-flow.org/guide/basic/keyboard.html
             */
            const keyboardConfig = {
            // 启用键盘快捷键
            enabled: true, // option value: true | false
            }

            /**
             * 网格配置 参考 http://logic-flow.org/guide/basic/grid.html
             */
            const gridConfig = {
            // 启用网格
            enabled: true, // option value: true | false

            // 设置网格大小
            size: 10, // option value: number

            // 设置是否可见，若设置为 false 则不显示网格线但是仍然保留 size 栅格的效果
            visible: true, // option value: true | false

            // 设置网格类型，目前支持 dot 点状和 mesh 线状两种
            type: 'mesh', // option value: 'dot' | 'mesh'
            config: {
                // 设置网格的颜色
                color: '#eeeeee', // option value: HEX | RGB

                // 设置网格线的宽度
                thickness: 1, // option value: number
            }
            }

            /**
             * 杂项配置
             */
            const extraConfig = {
            // 选区灵敏度
            SelectionSense: {
                // 是否要边的起点终点都在选区范围才算选中
                isWholeEdge: true, // option value: true | false

                // 是否要节点的全部点都在选区范围才算选中
                isWholeNode: true, // option value: true | false
            },

            // 默认连线类型
            defaultEdgeType: 'line' // option value: 'line' | 'polyline' | 'bezier' | 自定义连线类型
        }
        ///////////////////// src/components/logicflowConfig/common/commonConfig.js end ////////////////

        ////////////////////////////src/components/logicflowConfig/common/methods.js start ////////////////
        const WIDTH = 100
        const HEIGHT = 30

        function getuuid (index) {
        if (!index) {
            index = 0
        }
        return index.toString(16) + "-" + Date.now().toString(16)
        }

        function getScale (nodes_pure, idMap_pure, nodes, idMap, k, maxDepth, w) {
        if (nodes[k].type != "sub-system") {
            let j = idMap_pure.indexOf(nodes[k].id)
            return [
            parseInt((nodes_pure[j].depth / maxDepth) * w * 0.8) - WIDTH / 2,
            parseInt((nodes_pure[j].depth / maxDepth) * w * 0.8) + WIDTH / 2,
            parseInt((nodes_pure[j].x0 + nodes_pure[j].x1) * 0.4) - HEIGHT / 2,
            parseInt((nodes_pure[j].x0 + nodes_pure[j].x1) * 0.4) + HEIGHT / 2
            ]
        } else {
            let allScale = nodes[k].children.map(itm => { return getScale(nodes_pure, idMap_pure, nodes, idMap, idMap.indexOf(itm), maxDepth, w) })
            return [
            Math.min(...allScale.map(itm => itm[0])) - 50,
            Math.max(...allScale.map(itm => itm[1])) + 50,
            Math.min(...allScale.map(itm => itm[2])) - 50,
            Math.max(...allScale.map(itm => itm[3])) + 50
            ]
        }
        }

        function autoArrange (graphStruct, h, w) {
        let nodes_pure = graphStruct.nodes.filter(itm => {return itm.type !== "sub-system"})
        let nodes_ = nodes_pure.map(itm => {return {id: itm.id}})
        let idMap_pure = nodes_pure.map(itm => itm.id)
        let edges_ = graphStruct.edges.map(itm => {return {source: idMap_pure.indexOf(itm.sourceNodeId), target: idMap_pure.indexOf(itm.targetNodeId)}})
        let nodes = sankey().nodeWidth(WIDTH).nodePadding(10).size([h, w]).nodes(nodes_).links(edges_)().nodes
        var maxDepth = Math.max(1, ...nodes.map(itm => itm.depth))
        let idMap = graphStruct.nodes.map(itm => itm.id)
        graphStruct.nodes.forEach((itm, k) => {
            if (itm.type !== "sub-system") {
            graphStruct.nodes[k].x = parseInt((nodes[k].depth / maxDepth) * w * 0.8)
            graphStruct.nodes[k].y = parseInt((nodes[k].x0 + nodes[k].x1) * 0.4)
            graphStruct.nodes[k].text.x = graphStruct.nodes[k].x + WIDTH / 2
            graphStruct.nodes[k].text.y = graphStruct.nodes[k].y
            } else {
            let recScale = getScale(nodes, idMap_pure, graphStruct.nodes, idMap, k, maxDepth, w)
            graphStruct.nodes[k].x = (recScale[0] + recScale[1]) / 2
            graphStruct.nodes[k].y = (recScale[2] + recScale[3]) / 2
            graphStruct.nodes[k].properties.nodeSize.width = recScale[1] - recScale[0]
            graphStruct.nodes[k].properties.nodeSize.height = recScale[3] - recScale[2]
            }
        })
        graphStruct.edges.forEach((itm, k) => {
            let Sindex = idMap.indexOf(graphStruct.edges[k].sourceNodeId)
            let Eindex = idMap.indexOf(graphStruct.edges[k].targetNodeId)
            if (graphStruct.nodes[Sindex].type == "switch-node") {
            graphStruct.edges[k].startPoint.x = graphStruct.nodes[Sindex].x + WIDTH / 2
            graphStruct.edges[k].startPoint.y = graphStruct.nodes[Sindex].y
            graphStruct.edges[k].endPoint.x = graphStruct.nodes[Eindex].x
            if (graphStruct.nodes[Sindex].y > graphStruct.edges[k].endPoint.y) {
                graphStruct.edges[k].endPoint.y = graphStruct.nodes[Eindex].y + HEIGHT / 2
            } else {
                graphStruct.edges[k].endPoint.y = graphStruct.nodes[Eindex].y - HEIGHT / 2
            }
            graphStruct.edges[k].pointsList = []
            } else {
            graphStruct.edges[k].startPoint.x = graphStruct.nodes[Sindex].x + WIDTH / 2
            graphStruct.edges[k].startPoint.y = graphStruct.nodes[Sindex].y
            graphStruct.edges[k].endPoint.x = graphStruct.nodes[Eindex].x - WIDTH / 2
            graphStruct.edges[k].endPoint.y = graphStruct.nodes[Eindex].y
            graphStruct.edges[k].pointsList = []
            }
        })
        return graphStruct
        }

        function exportStruct (graphStruct) {
        let nodeList = []
        let linkList = []
        let idMap = {}

        graphStruct.nodes.forEach(itm => {
            idMap[itm.id] = itm.text.value
        })

        graphStruct.nodes.forEach(itm => {
            let node = {
            text: itm.text.value,
            type: itm.type,
            showConfig: {
                position: {x: itm.x, y: itm.y},
                properties: itm.properties,
            }
            }

            if (itm.type=="switch-node") {
            node.control = idMap[itm.properties.control]
            node.switchType = itm.properties.normalState ? "常闭" : "常开"
            } else if (itm.type=="sub-system") {
            node.children = itm.children.map(itm_ => { return idMap[itm_] })
            } else {

            }
            nodeList.push(node)
        })

        graphStruct.edges.forEach(itm => {
            linkList.push({
            id: itm.id,
            from: idMap[itm.sourceNodeId],
            to: idMap[itm.targetNodeId],
            type: itm.type,
            showConfig: {
                startAnchor: itm.startPoint,
                endAnchor: itm.endPoint,
                interAnchors: itm.pointsList,
                properties: itm.properties,
            },
            })
        })
        return {
            nodes: nodeList,
            edges: linkList
        }
        }

        function importStruct (graphStruct, showType='edit', h=900, w=1200) {
        let nodeList = []
        let linkList = []
        let idMap = []
        let count = 0
        let autopos = false

        graphStruct.nodes.forEach(itm => {
            count += 1
            idMap[itm.text] = getuuid(count * 2)
        })

        graphStruct.nodes.forEach(itm => {
            if (itm.showConfig.position.x === null || typeof itm.showConfig.position.x === "undefined" || itm.showConfig.position.y === null || typeof itm.showConfig.position.y === "undefined") {
            autopos = true
            }
            let node = {
            id: idMap[itm.text],
            text: {
                value: itm.text,
                x: itm.showConfig.position.x + WIDTH / 2,
                y: itm.showConfig.position.y,
            },
            type: itm.type,
            x: itm.showConfig.position.x,
            y: itm.showConfig.position.y,
            properties: itm.showConfig.properties,
            }
            // show type config
            node.properties.showType = showType
            if (showType === 'check') {
            node.properties.collision = itm.collision
            node.properties.fuzzible = itm.fuzzible
            node.properties.detectable = itm.detectable
            } else if (showType === 'analyse') {
            node.properties.state = itm.state
            node.properties.fuzzy_state = itm.fuzzy_state
            }
            // special properties
            if (itm.type == "switch-node") {
            node.properties.control = idMap[itm.control]
            node.properties.normalState = itm.switchType.includes("闭")
            } else if (itm.type == "sub-system") {
            node.children = itm.children.map(itm_ => { return idMap[itm_] })
            } else {

            }
            nodeList.push(node)
        })
        graphStruct.edges.forEach(itm => {
            count += 1
            linkList.push({
            id: idMap[itm.from] + "-" + idMap[itm.to] + "-" + getuuid(count * 2 + 1),
            sourceNodeId: idMap[itm.from],
            targetNodeId: idMap[itm.to],
            type: itm.type,
            startPoint: itm.showConfig.startAnchor,
            endPoint: itm.showConfig.endAnchor,
            pointsList: itm.showConfig.interAnchors,
            properties: itm.showConfig.properties,
            })
        })

        let _graphStruct = {
            nodes: nodeList,
            edges: linkList
        }

        if (autopos) {
            _graphStruct = autoArrange(_graphStruct, h, w)
        }

        return _graphStruct
        }

        function getColorRGB_check (collision, fuzzible, detectable, typeColor) {
        if (collision) {
            return "rgb(255, 0, 0)"
        } else if (fuzzible) {
            return "rgb(0, 0, 255)"
        } else if (!detectable) {
            return "rgb(150, 150, 150)"
        } else {
            return typeColor //Replace by Type-Defined Color
        }
        }

        function getColorRGB_analyse (state, fuzzy_state, fuzzy_ratio) {
        if (fuzzy_state == null) {
            fuzzy_state = 0
        }
        if (fuzzy_ratio == null) {
            fuzzy_ratio = 0.75
        }
        fuzzy_state *= fuzzy_ratio
        if (state == null) {
            return "rgb(150, 150, 150)"
        } else if (state < 0.5) {
            return 'rgb(' + parseInt((1 - fuzzy_state) * state * 255 + fuzzy_state * 128) + ","
                        + parseInt((1 - fuzzy_state) * 255 + fuzzy_state * 128) + ","
                        + parseInt(fuzzy_state * 128)  + ")"
        } else {
            return 'rgb(' + parseInt((1 - fuzzy_state) * 255 + fuzzy_state * 128) + ","
                        + parseInt((1 - fuzzy_state) * (1 - state) * 255 + fuzzy_state * 128) + ","
                        + parseInt(fuzzy_state * 128)  + ")"
        }
        }

        function getColorRGB_check_sub (collision, fuzzible, detectable, typeColor) {
        if (collision) {
            return "rgba(255, 0, 0, 0.45)"
        } else if (fuzzible) {
            return "rgba(0, 0, 255, 0.45)"
        } else if (!detectable) {
            return "rgba(150, 150, 150, 0.45)"
        } else {
            return typeColor //Replace by Type-Defined Color
        }
        }

        function getColorRGB_analyse_sub (state, fuzzy_state, fuzzy_ratio) {
        if (fuzzy_state == null) {
            fuzzy_state = 0
        }
        if (fuzzy_ratio == null) {
            fuzzy_ratio = 0.75
        }
        fuzzy_state *= fuzzy_ratio
        if (state == null) {
            return "rgba(150, 150, 150, 0.45)"
        } else if (state < 0.5) {
            return 'rgba(' + parseInt((1 - fuzzy_state) * state * 255 + fuzzy_state * 128) + ","
                        + parseInt((1 - fuzzy_state) * 255 + fuzzy_state * 128) + ","
                        + parseInt(fuzzy_state * 128)  + ", 0.45)"
        } else {
            return 'rgba(' + parseInt((1 - fuzzy_state) * 255 + fuzzy_state * 128) + ","
                        + parseInt((1 - fuzzy_state) * (1 - state) * 255 + fuzzy_state * 128) + ","
                        + parseInt(fuzzy_state * 128)  + ", 0.45)"
        }
        }

        function getBytesLength (word) {
        if (!word) {
            return 0
        }
        let totalLength = 0
        for (let i = 0; i < word.length; i++) {
            const c = word.charCodeAt(i)
            if ((word.match(/[A-Z]/))) {
            totalLength += 1.5
            } else if ((c >= 0x0001 && c <= 0x007e) || (c >= 0xff60 && c <= 0xff9f)) {
            totalLength += 1
            } else {
            totalLength += 1.8
            }
        }
        return totalLength
        }
        //////////////////////////src/components/logicflowConfig/common/methods.js end///////////////////

        //////////////////////////////////////////////node start //////////////////////////
       // basenode.js
        class RedNodeModel extends RectNodeModel {
            /**
             * 初始化
             */
            initNodeData (data) {
                super.initNodeData(data)
                this.width = 100
                this.height = 30
                this.radius = 5
                this.iconPosition = '' // icon位置，left表示左边，'right'表示右边
                this.defaultFill = this.properties.typeColor
            }
            /**
             * 动态设置数据，区别于初始化的数据设定，该部分会响应数据变化(每次properties发生变化会触发)
             */
            setAttributes () {
                if (this.text.value) {
                let width = 30 + getBytesLength(this.text.value) * 9
                width = Math.ceil(width / 20) * 20
                if (width < 100) {
                    width = 100
                }
                this.width = width
                this.properties.width = width
                }
                this.text.x = this.x + 10
                this.text.y = this.y
                if (this.properties.showType === 'check') {
                this.defaultFill = getColorRGB_check(this.properties.collision, this.properties.fuzzible, this.properties.detectable, this.properties.typeColor)
                } else if (this.properties.showType === 'analyse') {
                this.defaultFill = getColorRGB_analyse(this.properties.state, this.properties.fuzzy_state)
                }
            }
            updateText (val) {
                super.updateText(val)
                this.setAttributes()
            }
            /**
             * 重写节点样式
             */
            getNodeStyle () {
                const style = super.getNodeStyle()
                const dataStyle = this.properties.style || {}
                if (this.isSelected) {
                style.strokeWidth = Number(dataStyle.borderWidth) || 2
                style.stroke = dataStyle.borderColor || '#ff7f0e'
                } else {
                style.strokeWidth = Number(dataStyle.borderWidth) || 1
                style.stroke = dataStyle.borderColor || '#999'
                }
                style.fill = dataStyle.backgroundColor || this.defaultFill
                return style
            }
            /**
             * 重写定义锚点
             */
            getDefaultAnchor () {
                const { x, y, id, width, height } = this
                const anchors = [
                {
                    x: x + width / 2,
                    y: y,
                    id: `${id}_right`,
                    type: "right"
                },
                {
                    x: x - width / 2,
                    y: y,
                    id: `${id}_left`,
                    type: "left"
                }
                ]
                return anchors
            }
            /**
             *
             */
            getOutlineStyle () {
                const style = super.getOutlineStyle()
                style.stroke = 'transparent'
                style.hover.stroke = 'transparent'
                return style
            }
            /**
             * 导出时处理导出的数据
             */
            getData () {
                const data = super.getData()
                data.properties.ui = 'node-red'
                return data
            }
            }


        class RedNodeView extends RectNode {
            /**
             * 1.1.7版本后支持在view中重写锚点形状。
             * 重写锚点新增
             */
            getAnchorShape (anchorData) {
                const { x, y, type } = anchorData
                return h("rect", {
                x: x - 5,
                y: y - 5,
                width: 10,
                height: 10,
                className: 'custom-anchor'
                })
            }
            getShape () {
                const {
                text,
                x,
                y,
                width,
                height,
                radius
                } = this.props.model
                const style = this.props.model.getNodeStyle()
                return h(
                'g',
                {
                    className: 'lf-red-node'
                },
                [
                    h('rect', {
                    ...style,
                    x: x - width / 2,
                    y: y - height / 2,
                    width,
                    height,
                    rx: radius,
                    ry: radius
                    }),
                    h('g', {
                    style: 'pointer-events: none',
                    transform: `translate(${x}, ${y})`
                    }, [
                    h('rect', {
                        x: - width / 2,
                        y: - height / 2,
                        width: 30,
                        height: 30,
                        fill: '#000',
                        fillOpacity: 0.05,
                        stroke: 'none',
                    }),
                    this.getIcon(),
                    h('path', {
                        d: `M ${30 - width / 2} ${1 -height / 2 } l 0 28`,
                        stroke: '#000',
                        strokeOpacity: 0.1,
                        strokeWidth: 1
                    })
                    ])
                ]
                )
            }
            getIcon () {
                const { width, height } = this.props.model
                return h('image', {
                width: 30,
                height: 30,
                x: - width / 2,
                y: - height / 2,
                href: this.props.model.properties.icon
                })
            }
        }

        const BaseNode = {
            type: 'red-node',
            model: RedNodeModel,
            view: RedNodeView
            }

                    // FaultNode.js
        class FaultNodeModel extends BaseNode.model {
            initNodeData (data) {
                super.initNodeData(data)
            }
            setAttributes () {
                super.setAttributes()
                const circleOnlyAsTarget = {
                message: "只允许从右边的锚点连出",
                validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
                    return sourceAnchor.type === "right"
                }
                }
                this.sourceRules.push(circleOnlyAsTarget)
                this.targetRules.push({
                message: "只允许连接左边的锚点",
                validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
                    return targetAnchor.type === "left"
                }
                })
            }
        }   

        class FaultNodeView extends BaseNode.view {}

        const FaultNode = {
        type: 'fault-node',
        model: FaultNodeModel,
        view: FaultNodeView
        }   

        // SwitchNode.js
        const typeColor_open = '#96c24e'
        const typeColor_close = '#E74C3C'

        class SwitchNodeModel extends BaseNode.model {
        initNodeData (data) {
            super.initNodeData(data)
        }
        setAttributes () {
            super.setAttributes()
            if (this.properties.normalState) {
            this.defaultFill = typeColor_open
            } else {
            this.defaultFill = typeColor_close
            }
            const circleOnlyAsTarget = {
            message: "只允许从右边的锚点连出",
            validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
                return sourceAnchor.type === "right"
            }
            }
            this.sourceRules.push(circleOnlyAsTarget)
            this.targetRules.push({
            message: "只允许连接左边或上下两侧的锚点",
            validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
                return targetAnchor.type === "left" || 'top' || 'bottom'
            }
            })
        }
        getDefaultAnchor () {
            const { x, y, id, width, height } = this
            const anchors = [
            {
                x: x + width / 2,
                y: y,
                id: `${id}_right`,
                type: "right"
            },
            {
                x: x - width / 2,
                y: y,
                id: `${id}_left`,
                type: "left"
            },
            {
                x: x,
                y: y - height / 2,
                id: `${id}_top`,
                type: "top"
            },
            {
                x: x,
                y: y + height / 2,
                id: `${id}_bottom`,
                type: "bottom"
            }
            ]
            return anchors
        }
        }

        class SwitchNodeView extends BaseNode.view {}        

        const SwitchNode = {
        type: 'switch-node',
        model: SwitchNodeModel,
        view: SwitchNodeView
        }
        // TestNode.js

        class TestNodeModel extends BaseNode.model {
            initNodeData (data) {
                super.initNodeData(data)
            }
            setAttributes () {
                super.setAttributes()
                this.targetRules.push({
                message: "只允许连接左边的锚点",
                validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
                    return targetAnchor.type === "left"
                }
                })
            }
            getDefaultAnchor () {
                const { x, y, id, width, height } = this
                const anchors = [
                {
                    x: x - width / 2,
                    y: y,
                    id: `${id}_left`,
                    type: "left"
                }
                ]
                return anchors
            }
        }

        class TestNodeView extends BaseNode.view {}

        const TestNode = {
        type: 'test-node',
        model: TestNodeModel,
        view: TestNodeView
        }
        // algorithmNode.js
        class AlgorithmNodeModel extends BaseNode.model {
            initNodeData (data) {
                super.initNodeData(data)
            }
            setAttributes () {
                super.setAttributes()
                const circleOnlyAsTarget = {
                message: "只允许从右边的锚点连出",
                validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
                    return sourceAnchor.type === "right"
                }
                }
                this.sourceRules.push(circleOnlyAsTarget)
                this.targetRules.push({
                message: "只允许连接左边的锚点",
                validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
                    return targetAnchor.type === "left"
                }
                })
            }
        }


        class AlgorithmNodeView extends BaseNode.view {} 
        
        const AlgorithmNode = {
        type: 'algorithm-node',
        model: AlgorithmNodeModel,
        view: AlgorithmNodeView
        }        

        // group Node.js
        class SelectGroupModel extends GroupNode.model {
            initNodeData (data) {
                super.initNodeData(data)
                this.resizable = true
                this.foldable = true
                this.foldedWidth = 175
                this.foldedHeight = 40
                const forbidConnect = {
                message: "不允许直接连接到子系统",
                validate: () => {
                    return false
                }
                }
                this.targetRules.push(forbidConnect)
                if (data.properties.isFold === true) {
                setTimeout(() => {
                    this.foldGroup(true)
                })
                }
            }
            setAttributes () {
                this.text.x = this.x + 10
                if (this.isFolded) {
                this.text.y = this.y
                } else {
                this.text.y = this.y - this.properties.nodeSize.height / 2 + 14
                }
            }
            updateText (value) {
                const textValue = value.replace(/\n/gm, "")
                super.updateText(textValue.slice(0, 10))
            }
            getNodeStyle () {
                const style = super.getNodeStyle()
                style.stroke = "#0998FF"
                style.strokeWidth = 1
                style.strokeDasharray = "3 3"
                if (this.properties.showType === 'check') {
                style.fill = getColorRGB_check_sub(this.properties.collision, this.properties.fuzzible, this.properties.detectable, "rgba(239, 245, 255, 0.45)")
                } else if (this.properties.showType === 'analyse') {
                style.fill = getColorRGB_analyse_sub(this.properties.state, this.properties.fuzzy_state)
                } else {
                style.fill = "rgba(239, 245, 255, 0.45)"
                }
                return style
            }
            getTextStyle () {
                const style = super.getTextStyle()
                style.fontSize = 14
                style.color = "#3C96FE"
                style.fontWeight = 600
                style.lineHeight = 16
                return style
            }
            foldGroup (isFolded) {
                super.foldGroup(isFolded)
                if (isFolded) {
                this.text.x = this.x + 10
                this.text.y = this.y
                } else {
                this.width = this.properties.nodeSize.width
                this.height = this.properties.nodeSize.height
                this.text.x = this.x + 10
                this.text.y = this.y - this.height / 2 + 14
                }
            }
            deleteGroup () {
                this.graphModel.deleteNode(this.id)
            }
            getData () {
                const data = super.getData()
                if (this.isFolded) {
                data.x = data.x + this.unfoldedWidth / 2 - this.width / 2
                data.y = data.y + this.unfoldedHight / 2 - this.height / 2
                data.properties.isFold = this.isFolded
                data.width = this.unfoldedWidth
                data.height = this.unfoldedHight
                } else {
                data.properties.isFold = this.isFolded
                data.width = this.width
                data.height = this.height
                }
                return data
            }
            }

        class SelectGroupView extends GroupNode.view {
            getFoldIcon () {
                const { model, graphModel } = this.props
                const foldX = model.x - model.width / 2 + 5
                const foldY = model.y - model.height / 2 + 5
                if (!model.foldable) return null
                // 展开的icon
                const unFoldIcon = h(
                "svg",
                {
                    transform: "translate(329.000000, 311.000000)",
                    x: foldX,
                    y: foldY,
                    width: 20,
                    height: 20,
                    fill: "#3C96FE"
                },
                [
                    h("path", {
                    "pointer-events": "none",
                    d:
                        "M10.5097656,8.61914062 L16.8066406,2.32226563 C16.8554688,2.2734375 16.8203125,2.18945313 16.7519531,2.18945313 L13.6035156,2.18945313 C13.2636719,2.18945313 12.9746094,1.921875 12.9648438,1.58203125 C12.9550781,1.22851563 13.2382813,0.939453125 13.5898438,0.939453125 L18.4492188,0.939453125 C18.7949219,0.939453125 19.0742188,1.21875 19.0742188,1.56445313 L19.0742188,6.41015625 C19.0742188,6.75 18.8066406,7.0390625 18.4667969,7.04882813 C18.1132813,7.05859375 17.8242188,6.77539063 17.8242188,6.42382813 L17.8242188,3.25976563 C17.8242188,3.18945313 17.7402344,3.15625 17.6914063,3.20507813 L11.3925781,9.50195312 C11.1484375,9.74609375 10.7519531,9.74609375 10.5078125,9.50195312 C10.265625,9.2578125 10.265625,8.86328125 10.5097656,8.61914062 Z M0.921875,18.4375 L0.921875,13.5917969 C0.921875,13.2519531 1.18945312,12.9628906 1.52929688,12.953125 C1.8828125,12.9433594 2.171875,13.2265625 2.171875,13.578125 L2.171875,16.7402344 C2.171875,16.8105469 2.25585938,16.84375 2.3046875,16.7949219 L8.6015625,10.4980469 C8.72460938,10.375 8.8828125,10.3144531 9.04296875,10.3144531 C9.203125,10.3144531 9.36328125,10.375 9.484375,10.4980469 C9.72851562,10.7421875 9.72851562,11.1386719 9.484375,11.3828125 L3.18945312,17.6796875 C3.140625,17.7285156 3.17578125,17.8125 3.24414063,17.8125 L6.39257812,17.8125 C6.73242187,17.8125 7.02148438,18.0800781 7.03125,18.4199219 C7.04101562,18.7734375 6.7578125,19.0625 6.40625,19.0625 L1.546875,19.0625 C1.20117188,19.0625 0.921875,18.7832031 0.921875,18.4375 Z M1.25,6.875 L0,6.875 L0,0 L6.875,0 L6.875,1.25 L1.25,1.25 L1.25,6.875 Z M20,20 L13.125,20 L13.125,18.75 L18.75,18.75 L18.75,13.125 L20,13.125 L20,20 Z"
                    })
                ]
                )
                // 收起的icon
                const foldIcon = h(
                "svg",
                {
                    transform: "translate(330.000000, 495.000000)",
                    x: foldX,
                    y: foldY,
                    width: 20,
                    height: 20,
                    fill: "#3C96FE"
                },
                [
                    h("path", {
                    "pointer-events": "none",
                    d:
                        "M18.890625,2.00390625 L12.59375,8.30273438 C12.5449219,8.3515625 12.5800781,8.43554688 12.6484375,8.43554688 L15.796875,8.43554688 C16.1367187,8.43554688 16.4257812,8.703125 16.4355469,9.04296875 C16.4453125,9.39648438 16.1621094,9.68554688 15.8105469,9.68554688 L10.9511719,9.68554688 C10.6054687,9.68554688 10.3261719,9.40625 10.3261719,9.06054688 L10.3261719,4.21484375 C10.3261719,3.875 10.59375,3.5859375 10.9335938,3.57617188 C11.2871094,3.56640625 11.5761719,3.84960938 11.5761719,4.20117188 L11.5761719,7.36328125 C11.5761719,7.43359375 11.6601562,7.46679688 11.7089844,7.41796875 L18.0078125,1.12109375 C18.2519531,0.876953125 18.6484375,0.876953125 18.8925781,1.12109375 C19.1347656,1.36523438 19.1347656,1.75976562 18.890625,2.00390625 L18.890625,2.00390625 Z M9.66992188,10.9394531 L9.66992188,15.7851562 C9.66992188,16.125 9.40234375,16.4140625 9.0625,16.4238281 C8.70898438,16.4335938 8.41992188,16.1503906 8.41992188,15.7988281 L8.41992188,12.6367188 C8.41992188,12.5664062 8.3359375,12.5332031 8.28710938,12.5820312 L1.98828125,18.8789062 C1.86523438,19.0019531 1.70703125,19.0625 1.546875,19.0625 C1.38671875,19.0625 1.2265625,19.0019531 1.10546875,18.8789062 C0.861328125,18.6347656 0.861328125,18.2382812 1.10546875,17.9941406 L7.40234375,11.6972656 C7.45117188,11.6484375 7.41601563,11.5644531 7.34765625,11.5644531 L4.19921875,11.5644531 C3.859375,11.5644531 3.5703125,11.296875 3.56054688,10.9570312 C3.55078125,10.6035156 3.83398438,10.3144531 4.18554688,10.3144531 L9.04492188,10.3144531 C9.390625,10.3144531 9.66992188,10.59375 9.66992188,10.9394531 Z M1.25,6.875 L0,6.875 L0,0 L6.875,0 L6.875,1.25 L1.25,1.25 L1.25,6.875 Z M20,20 L13.125,20 L13.125,18.75 L18.75,18.75 L18.75,13.125 L20,13.125 L20,20 Z"
                    })
                ]
                )
                return h('g', {}, [
                h('rect', {
                    height: 20,
                    width: 20,
                    rx: 2,
                    ry: 2,
                    strokeWidth: 1,
                    fill: 'transparent',
                    stroke: 'transparent',
                    cursor: 'pointer',
                    x: model.x - model.width / 2 + 5,
                    y: model.y - model.height / 2 + 5,
                    onClick: () => {
                    model.foldGroup(!model.properties.isFolded)
                    graphModel.eventCenter.emit('group:fold', model)
                    }
                }),
                model.properties.isFolded ? unFoldIcon : foldIcon
                ])
            }

            getDeleteIcon () {
                const { model } = this.props
                return h("g", {}, [
                h("rect", {
                    height: 20,
                    width: 20,
                    rx: 2,
                    ry: 2,
                    strokeWidth: 1,
                    fill: "transparent",
                    stroke: "transparent",
                    cursor: "pointer",
                    x: model.x + model.width / 2 + 14,
                    y: model.y - model.height / 2 + 5,
                    onClick: () => {
                    model.deleteGroup()
                    }
                }),
                h(
                    "svg",
                    {
                    transform: "translate(1.000000, 1.000000)",
                    fill: "#3C96FE",
                    x: model.x + model.width / 2 + 14,
                    y: model.y - model.height / 2 + 5,
                    width: 20,
                    height: 20
                    },
                    [
                    h("path", {
                        "pointer-events": "none",
                        d:
                        "M15.3,1.4 L12.6,1.4 L12.6,0 L5.4,0 L5.4,1.4 L0,1.4 L0,2.8 L2,2.8 L2,17.3 C2,17.6865993 2.31340068,18 2.7,18 L15.3,18 C15.6865993,18 16,17.6865993 16,17.3 L16,2.8 L18,2.8 L18,1.4 L15.3,1.4 Z M14.6,16.6 L3.4,16.6 L3.4,2.8 L14.6,2.8 L14.6,16.6 Z"
                    }),
                    h("path", {
                        "pointer-events": "none",
                        d:
                        "M6,5.4 L7.4,5.4 L7.4,14.4 L6,14.4 L6,5.4 Z M10.6,5.4 L12,5.4 L12,14.4 L10.6,14.4 L10.6,5.4 Z"
                    })
                    ]
                )
                ])
            }
            getResizeShape () {
                return h("g", {}, [super.getResizeShape(), this.getDeleteIcon()])
            }
            }
        
        const SubSystem = {
        type: "sub-system",
        model: SelectGroupModel,
        view: SelectGroupView
        }

        //////////////////////////////////////////////node end //////////////////////////

        //////////////////// src/components/logicflowConfig/init.js start//////////////////////////////////
        /**
         * 公有初始化属性
         */
         const common = {
        showType: 'edit',
        collision : false,
        detectable: true,
        fuzzible: false,
        fuzzy_state: 0,
        state: 0
        }

        /**
         * 节点初始化
         */
        const nodeList = [
        {
            type: 'test-node',
            text: '测试',
            properties: {
            // common
            ...common,
            icon: "/static/images/delay.svg",
            typeColor: '#eea2a4',
            // special
            }
        },
        {
            type: 'fault-node',
            text: '故障',
            properties: {
            // common
            ...common,
            icon: "/static/images/delay.svg",
            typeColor: '#edc3ae',
            // special
            flevel: 0, //故障等级
            }
        },
        {
            type: 'switch-node',
            text: '开关',
            properties: {
            // common
            ...common,
            icon: "/static/images/delay.svg",
            typeColor: '#96c24e',
            // special
            normalState: true, // true | false 开关常态（常开|常闭）
            control: null, // 控制节点
            }
        },
        {
            type: 'algorithm-node',
            text: '算法',
            properties: {
            // common
            ...common,
            icon: "/static/images/delay.svg",
            typeColor: '#85C1E9',
            // special
            algorithm: 0, // 算法
            }
        }
        ]

        /**
         * 子系统初始化
         */
        const subsystemInit = {
        type: "sub-system",
        text: '子系统',
        properties: {
            ...common
        }
        }
        //////////////////// src/components/logicflowConfig/init.js end//////////////////////////////////

        ///////////////////src/components/logicflowConfig/tools/util.js start ///////////////////

        const utils = {
            // createSub () {
            //     const { nodes } = this.lf.getSelectElements()
            //     if (nodes.length === 0) {
            //     this.$alert('未选中任何节点，无法创建子系统！')
            //     } else {
            //     this.lf.clearSelectElements()
            //     const { startPoint, endPoint } = this.lf.extension.selectionSelect
            //     // startPoint 和 endPoint 是 dom 坐标，需要转换成 canvas 坐标绘制
            //     const { transformModel } = this.lf.graphModel
            //     const [x1, y1] = transformModel.HtmlPointToCanvasPoint([
            //         startPoint.x,
            //         startPoint.y
            //     ])
            //     const [x2, y2] = transformModel.HtmlPointToCanvasPoint([
            //         endPoint.x,
            //         endPoint.y
            //     ])
            //     const width = x2 - x1
            //     const height = y2 - y1
            //     let node = {...subsystemInit}
            //     node.x = x1 + width / 2
            //     node.y = y1 + height / 2
            //     node.properties.nodeSize = {
            //         width: width,
            //         height: height
            //     }
            //     node.children = nodes.map((item) => item.id)
            //     this.lf.addNode(node)
            //     this.lf.render(this.$data.lf.getGraphRawData())
            //     }
            // },
            // foldAllChild (child) {
            //     child.forEach((nodeID) => {
            //     let node = this.lf.graphModel.getNodeModelById(nodeID)
            //     if (node.type === 'sub-system') {
            //         if (node.isFolded === false) {
            //         this.foldAllChild(node.children)
            //         node.foldGroup(true)
            //         }
            //         if (node.isFolded === true) {
            //         node.foldGroup(false)
            //         this.foldAllChild(node.children)
            //         node.foldGroup(true)
            //         }
            //     }
            //     })
            // },
            // unfoldAllChild (child) {
            //     child.forEach((nodeID) => {
            //     let node = this.lf.graphModel.getNodeModelById(nodeID)
            //     if (node.type === 'sub-system') {
            //         node.foldGroup(false)
            //         this.unfoldAllChild(node.children)
            //     }
            //     })
            // },
        }

        ///////////////////src/components/logicflowConfig/tools/util.js end ///////////////////

        //////////////////// src/components/logicflowConfig/tools/listeners.js start/////////////
        const listeners = {
        $_LfEvent () {
            // // 单击节点 测试用
            // this.lf.on('node:click', ({ data }) => {
            // console.log(data) // for test
            // })
            // // 双击节点
            // this.lf.on('node:dbclick', ({ data }) => {
            // this.formData = data
            // this.dialogVisible = true
            // })
            // // ※节点信息编辑
            // this.lf.on('node:edit', (data) => {
            // this.formData = data
            // this.dialogVisible = true
            // })
            // // 鼠标进入节点
            // this.lf.on('node:mouseenter', ({ data }) => {
            // if (data.properties.showType === 'analyse' && data.type !== 'sub-system') {
            //     const { transformModel } = this.lf.graphModel
            //     const [x, y] = transformModel.CanvasPointToHtmlPoint([
            //     data.x,
            //     data.y
            //     ])
            //     let left = x + 150 + data.properties.width / 2
            //     let top = y - 37
            //     let element = document.createElement('div')
            //     element.className = "popper"
            //     element.style.left = `${left}px`
            //     element.style.top = `${top}px`
            //     // let para = document.createElement('p')
            //     // para.appendChild(document.createTextNode(`名称: ${data.text.value}`))
            //     // element.appendChild(para)
            //     // para = document.createElement('p')
            //     // para.appendChild(document.createTextNode(`故障分数: ${data.properties.state * 100}`))
            //     // element.appendChild(para)
            //     // para = document.createElement('p')
            //     // para.appendChild(document.createTextNode(`模糊分数: ${data.properties.fuzzy_state * 100}`))
            //     // element.appendChild(para)
            //     element.innerHTML = `
            //     <p>名称: ${data.text.value}</p>
            //     <p>故障分数: ${data.properties.state * 100}</p>
            //     <p>模糊分数: ${data.properties.fuzzy_state * 100}</p>
            //     `
            //     let view = document.querySelector('.logic-flow-view')
            //     view.appendChild(element)
            // }
            // })
            // // 鼠标离开节点
            // this.lf.on('node:mouseleave', (node) => {
            // let element = document.querySelector('.popper')
            // if (element) { element.remove() }
            // })
            // // 连线删除
            // this.lf.on('edge:delete', ({data}) => {
            // let node = this.lf.graphModel.getNodeModelById(data.targetNodeId)
            // if (node.type === 'switch-node' && node.properties.control === data.sourceNodeId) {
            //     node.properties.control = null
            // }
            // })
            // // 锚点连线拖动连线成功时触发，主要用于添加额外的连线验证
            // this.lf.on('anchor:drop', (data) => {
            // let model = data.edgeModel
            // let edges = this.lf.getEdgeModels({
            //     sourceNodeId: model.sourceNodeId,
            //     targetNodeId: model.targetNodeId
            // })
            // if (edges.length > 1) {
            //     this.$alert('重复连线')
            //     this.lf.deleteEdge(model.id)
            // } else {
            //     // 开关的控制节点接入
            //     if (model.targetNodeId + '_top' === model.targetAnchorId || model.targetNodeId + '_bottom' === model.targetAnchorId) {
            //     let node = this.lf.graphModel.getNodeModelById(model.targetNodeId)
            //     if (node.properties.control === null) {
            //         node.properties.control = model.sourceNodeId
            //     } else {
            //         this.$alert('该开关已有控制节点')
            //         this.lf.deleteEdge(model.id)
            //     }
            //     }
            // }
            // })
            // // ※子系统折叠 & 展开
            // this.lf.on('group:fold', (data) => {
            // if (data.isFolded === true) {
            //     data.foldGroup(false)
            //     this.foldAllChild(data.children)
            //     data.foldGroup(true)
            // } else {
            //     this.unfoldAllChild(data.children)
            // }
            // })
        }
        }
        //////////////////////// src/components/logicflowConfig/tools/listeners.js end //////////////

        /////////////////////// src/components/logicflowConfig/tools/menu.js start //////////////////
        const menu = {
            $_render () {
            // 自定义常规右键菜单
            this.lf.extension.menu.setMenuConfig({
            // 节点右键菜单
            nodeMenu: [
                {
                text: '删除',
                callback: (node) => {
                    this.lf.deleteNode(node.id)
                }
                },
                {
                text: '编辑节点属性',
                callback: (node) => {
                    this.lf.graphModel.eventCenter.emit('node:edit', node)
                }
                }
            ],
            // 边右键菜单
            edgeMenu: [
                {
                text: '删除',
                callback: (edge) => {
                    this.lf.deleteEdge(edge.id)
                }
                }
            ]
            })
            // 自定义选区右键菜单
            this.lf.extension.menu.setMenuByType({
            type: 'lf:defaultSelectionMenu',
            menu: [
                {
                text:'删除',
                callback: (select) => {
                    select.nodes.map((item) => {this.lf.deleteNode(item.id)})
                    select.edges.map((item) => {this.lf.deleteEdge(item.id)})
                }
                },
                {
                text:'分组',
                callback: this.createSub
                }
            ]
            })
            // 自定义子系统右键菜单
            this.lf.extension.menu.setMenuByType({
            type: 'sub-system',
            menu: [
                {
                text:'删除子系统',
                callback: (node) => {
                    this.lf.deleteNode(node.id)
                }
                },
                {
                text:'解除子系统',
                callback: (node) => {
                    let sub = this.lf.getNodeModelById(node.id)
                    let { nodes } = this.lf.getGraphRawData()
                    nodes.some((itm) => {
                    if (itm.type === 'sub-system' && itm.children.indexOf(node.id) !== -1) {
                        let parent = this.lf.getNodeModelById(itm.id)
                        node.children.forEach((child) => { parent.addChild(child) })
                        return true
                    } else {
                        return false
                    }
                    })
                    node.children.forEach((child) => { sub.removeChild(child) })
                    this.lf.deleteNode(node.id)
                }
                }
            ]
            })
            // 自定义侧边栏 参考 http://logic-flow.org/guide/extension/component-dnd-panel.html
            this.lf.extension.dndPanel.setPatternItems([
            {
                label: '选区',
                icon: '',
                callback: () => {
                this.lf.extension.selectionSelect.openSelectionSelect()
                this.lf.once('selection:selected', () => {
                    this.lf.extension.selectionSelect.closeSelectionSelect()
                })
                }
            },
            {
                label: '分组',
                icon: '',
                callback: this.createSub
            }
            ])
            this.lf.render(this.data)
            this.$_LfEvent()
            }
        }

        /**
         * 按钮组自定义，设置是否提供该功能的按钮
         */
        // const controlConfig = {
        // // 放大
        // zoomIn: true,  // option value: true | false

        // // 缩小
        // zoomOut: true,  // option value: true | false

        // // 大小适应
        // zoomReset: true,  // option value: true | false

        // // 定位还原
        // translateRest: true,  // option value: true | false

        // // 还原(大小&定位)
        // reset: true,  // option value: true | false

        // // 撤销
        // undo: true,  // option value: true | false

        // // 重做
        // redo: true,  // option value: true | false

        // // 清空
        // clear: true,  // option value: true | false

        // // 重绘
        // reDraw: true,  // option value: true | false

        // // 导出数据
        // exportData: true,  // option value: true | false

        // // 载入数据
        // importData: true,  // option value: true | false

        // // 绘图检查 ※需要与后台交互
        // check: true,  // option value: true | false

        // // 指标分析 ※需要与后台交互
        // analyse: true  // option value: true | false
        // }        

        /////////////////////// src/components/logicflowConfig/tools/menu.js end //////////////////


        //////////////////////  src/components/logicflowConfig/common/index.js start///////////////////

        const methods_LF = {
            $_initLf(){
            const lf = new LogicFlow({
                container: document.querySelector('#container'),
                width: 700,
                height: 600,
                plugins: [
                    Menu,
                    Group,
                    Snapshot,
                    DndPanel,
                    SelectionSelect
                ]
                });
                this.lf = lf
                this.$_registerNode()
            },
            $_registerNode () {
                this.lf.register(BaseNode)
                // this.lf.register(BaseNode)
                this.lf.register(TestNode)
                this.lf.register(FaultNode)
                this.lf.register(SwitchNode)
                this.lf.register(SubSystem)


                this.$_render()
            },            
            ...menu,
            ...listeners,
            ...utils        
        }
        ///////////////////// src/components/logicflowConfig/common/index.js end ///////////////////////


        var vm = new Vue({
            el: "#app",
            data(){
                return{
                    lf: null,
                    data:{
                        // 节点
                        nodes: [
                            {
                            id: '21',
                            type: 'rect',
                            x: 100,
                            y: 200,
                            text: '矩形节点',
                            },
                            {
                            id: '50',
                            type: 'circle',
                            x: 300,
                            y: 400,
                            text: '圆形节点',
                            },
                            {
                            id: '503',
                            type: 'red-node',
                            x: 500,
                            y: 300,
                            text: 'base',
                            typeColor: "#edc3ae",
                            },
                            {
                            id: '5034',
                            type: 'fault-node',
                            x: 500,
                            y: 500,
                            text: 'fault',
                            typeColor: "#edc3ae",
                            },
                            {
                            id: '503224',
                            type: 'test-node',
                            x: 650,
                            y: 500,
                            text: 'test',
                            typeColor: "#ed3333",
                            },
                            {
                            id: '503444',
                            type: 'switch-node',
                            x: 500,
                            y: 550,
                            text: 'switch',
                            typeColor: "#e233ae",
                            },
                            // {
                            // id: '503444434',
                            // type: 'sub-system',
                            // x: 500,
                            // y: 550,
                            // text: 'switch',
                            // typeColor: "#e233ae",
                            // },
                        //     {
                        //     id:'12344444',
                        //     text: "故障",
                        //     type: "fault-node",
                        //     showConfig: {
                        //         position: {
                        //             x: 380,
                        //             y: 210
                        //         },
                        //         properties: {
                        //             showType: "edit",
                        //             collision: false,
                        //             detectable: true,
                        //             fuzzible: false,
                        //             fuzzy_state: 0,
                        //             state: 0,
                        //             icon: "",
                        //             typeColor: "#edc3ae",
                        //             flevel: 0,
                        //             width: 100,
                        //             ui: node-red
                        //         }
                        //     }
                        // }

                        ],
                        // 边
                        edges: [
                            {
                            type: 'polyline',
                            sourceNodeId: '50',
                            targetNodeId: '21',
                            },
                        ],
                        }

                }
            },
            mounted(){
                this.$_initLf()
                // this.lf.render(this.data);
                // const lf = new LogicFlow({
                // container: document.querySelector('#container'),
                // width: 700,
                // height: 600,
                // plugins: [
                //     DndPanel
                // ]
                // });
                // this.lf = lf

                // this.lf.render(this.data);
            },
            methods: {
                ...methods_LF
            }
        })
    </script>
</body>
</html>

























    </script>
</body>
</html>


